from django.template import Context, loader, RequestContext
from django.db.models import Q
from datetime import datetime
from datetime import date
from datetime import time
from my_beancounter.models import *
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.contrib.auth.decorators import login_required

# File: strptime.py

import re
import string

MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
          "Sep", "Oct", "Nov", "Dec"]

SPEC = {
    # map formatting code to a regular expression fragment
    "%a": "(?P<weekday>[a-z]+)",
    "%A": "(?P<weekday>[a-z]+)",
    "%b": "(?P<month>[a-z]+)",
    "%B": "(?P<month>[a-z]+)",
    "%C": "(?P<century>\d\d?)",
    "%d": "(?P<day>\d\d?)",
    "%D": "(?P<month>\d\d?)/(?P<day>\d\d?)/(?P<year>\d\d)",
    "%e": "(?P<day>\d\d?)",
    "%h": "(?P<month>[a-z]+)",
    "%H": "(?P<hour>\d\d?)",
    "%I": "(?P<hour12>\d\d?)",
    "%j": "(?P<yearday>\d\d?\d?)",
    "%m": "(?P<month>\d\d?)",
    "%M": "(?P<minute>\d\d?)",
    "%p": "(?P<ampm12>am|pm)",
    "%R": "(?P<hour>\d\d?):(?P<minute>\d\d?)",
    "%S": "(?P<second>\d\d?)",
    "%T": "(?P<hour>\d\d?):(?P<minute>\d\d?):(?P<second>\d\d?)",
    "%U": "(?P<week>\d\d)",
    "%w": "(?P<weekday>\d)",
    "%W": "(?P<weekday>\d\d)",
    "%y": "(?P<year>\d\d)",
    "%Y": "(?P<year>\d\d\d\d)",
    "%%": "%"
}

class TimeParser:
    def __init__(self, format):
        # convert strptime format string to regular expression
        format = string.join(re.split("(?:\s|%t|%n)+", format))
        pattern = []
        try:
            for spec in re.findall("%\w|%%|.", format):
                if spec[0] == "%":
                    spec = SPEC[spec]
                pattern.append(spec)
        except KeyError:
            raise ValueError, "unknown specificer: %s" % spec
        self.pattern = re.compile("(?i)" + string.join(pattern, ""))
    def match(self, daytime):
        # match time string
        match = self.pattern.match(daytime)
        if not match:
            raise ValueError, "format mismatch"
        get = match.groupdict().get
        tm = [0] * 9
        # extract date elements
        y = get("year")
        if y:
            y = int(y)
            if y < 68:
                y = 2000 + y
            elif y < 100:
                y = 1900 + y
            tm[0] = y
        m = get("month")
        if m:
            if m in MONTHS:
                m = MONTHS.index(m) + 1
            tm[1] = int(m)
        d = get("day")
        if d: tm[2] = int(d)
        # extract time elements
        h = get("hour")
        if h:
            tm[3] = int(h)
        else:
            h = get("hour12")
            if h:
                h = int(h)
                if string.lower(get("ampm12", "")) == "pm":
                    h = h + 12
                tm[3] = h
        m = get("minute")
        if m: tm[4] = int(m)
        s = get("second")
        if s: tm[5] = int(s)
        # ignore weekday/yearday for now
        return tuple(tm)

def strptime(string, format="%a %b %d %H:%M:%S %Y"):
    return TimeParser(format).match(string)

if __name__ == "__main__":
    # try it out
    import time
    print strptime("2000-12-20 01:02:03", "%Y-%m-%d %H:%M:%S")
    print strptime(time.ctime(time.time()))
	
def htmlDates(theMonth,theYear):
	"""Build HTML Select dropdowns for month and year"""
	
	m = range(1,13)
	month = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
	monthOptions = '<option value="0">All</option>'
	monthList = zip(m,month)
	for [num,mon] in monthList:
		if num == int(theMonth):
			monthOptions += '<option value="%s" selected="selected">%s</option>\n' % (num,mon)
		else:
			monthOptions += '<option value="%s">%s</option>\n' % (num,mon)
	this_year = datetime.now().year
	years = range(this_year - 10,this_year + 1)
	yearOptions = ''
	for y in years:
		if y == int(theYear):
			yearOptions += '<option selected="selected" value="%s">%s</option>' % (y,y)
		else:
			yearOptions += '<option value="%s">%s</option>' % (y,y)
	return (monthOptions,yearOptions)
	
def buildDates(theMonth,theYear):
	"""Return datetime objects for the start and end of the month. If month is 0 return start and end of year"""
	
	if theMonth == 0:
		reportStart = datetime(*strptime(str(theYear) + '1' + '1','%Y%m%d')[0:5])
		reportEnd =  datetime(*strptime(str(theYear) + '12' + '31','%Y%m%d')[0:5])
	else:
		reportStart = datetime(*strptime(str(theYear) + str(theMonth) + '1','%Y%m%d')[0:5])
		if theMonth == 12:
			theYear = theYear + 1
		else:
			theMonth = theMonth + 1
			reportEnd = datetime(*strptime(str(theYear) + str(theMonth) + '1','%Y%m%d')[0:5])
	return (reportStart,reportEnd)

def sortby(nlist, n):
    nlist[:] = [(x[n], x) for x in nlist]
    nlist.sort(reverse=True)
    nlist[:] = [val for (key, val) in nlist]
	
def detailCategory(theMonth,theYear,catType):
	"""Get list of each category total for a given category type."""
	allCats = Category.objects.filter(type__exact=catType)
	total = 0.00
	catTotals = []
	catLabels = ""
	
	(report_start,report_end) = buildDates(theMonth,theYear)
	for c in allCats:
		currentTotal = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) &  Entry.objects.filter(category__id=c.id)
		catSum = 0.00
		for e in currentTotal:
			catSum = catSum + float(e.amount)
			
		catLabels += '{label: "' + c.name + '", v: ' + str(c.id) + '}, '
		catTotals += [catSum]
		catList = zip(allCats,catTotals)
		sortby(catList,1)
		total = total + catSum
	catLabels = catLabels.rstrip(', ')
	return (catLabels,catList,total)
	

	
@login_required
def overview(request):
	
	try:
		theMonth = int(request.GET['month'])
		theYear = int(request.GET['year'])
	except (KeyError):
		theMonth = datetime.now().month
		theYear = datetime.now().year
	
	(monthOptions,yearOptions) = htmlDates(theMonth,theYear)
	
	(income_labels,income_list,income) = detailCategory(theMonth,theYear,"INC")
	(cogs_labels,cogs_list,cogs) = detailCategory(theMonth,theYear,"COGS")
	(expense_labels,expense_list,expense) = detailCategory(theMonth,theYear,"EXP")
	
	cogs_plus_expense = cogs + expense
	net = "%.2f" % (income-cogs_plus_expense)
	title = "Overview"
	return render_to_response('overview.html', locals(),context_instance=RequestContext(request))


@login_required
def history(request):
	expense_cats = Category.objects.filter(type__exact='EXP')
	expense_list = {}
	months = []
	total = amount = 0.00
	this_id = -1
	start = True
	expenses = Entry.objects.filter(Q(category__type = 'EXP') | Q(category__type = 'COGS')).order_by('date')
	for e in expenses:
		month = str(e.date.month) + '/' + str(e.date.year)
		id = int(str(e.date.month) + str(e.date.year))
		if id != this_id:
			if start == True:
				start = False
			else:
				expense_list[this_id] = [this_month,amount,0]
				months.append(this_id)
			this_id = id
			this_month = month
			amount = 0
		amount += float(e.amount)
		total += float(e.amount)
	expense_list[this_id] = [this_month,amount,0]
	months.append(this_id)
	
	
	income_list = []
	total = 0.00
	amount = 0.00
	this_id = -1
	start = True
	incomes = Entry.objects.filter(category__type = 'INC').order_by('date')
	for e in incomes:
		month = str(e.date.month) + '/' + str(e.date.year)
		id = int(str(e.date.month) + str(e.date.year))
		if id != this_id:
			if start == True:
				start = False
			else:
				expense_list[this_id][2] = amount
				if this_id in months:
					i = 0
				else:
					months.append(id)
			this_id = id
			this_month = month
			amount = 0
		amount += float(e.amount)
		total += float(e.amount)
	expense_list[this_id][2] = amount
	if this_id in months:
		i = 0
	else:
		months.append(id)
	months.sort(reverse=True)
	history_labels = ''
	for m in months:
		history_labels += '{label: "' + expense_list[m][0] + '", v: ' + str(m) + '}, '
	history_labels.rstrip(', ')
	title = 'History'
	return render_to_response('history.html',locals(),context_instance=RequestContext(request))
		
@login_required
def incomeVsCost(request):
	try:
		theMonth = int(request.GET['month'])
		theYear = int(request.GET['year'])
	except (KeyError):
		theMonth = datetime.now().month
		theYear = datetime.now().year
	
	(monthOptions,yearOptions) = htmlDates(theMonth,theYear)
	(report_start,report_end) = buildDates(theMonth,theYear)
	cogsCats = Category.objects.filter(type__exact='COGS')
	total = 0.00
	linkNumber = 0
	Totals = []
	cTotals = []
	iTotals = []
	iCats = []
	cogsLabels = ""


	for c in cogsCats:
		cogsEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) &  Entry.objects.filter(category__id=c.id)
		cSum = 0.00
		#total for COGS category
		for e in cogsEntries:
			cSum = cSum + float(e.amount)
		cTotals += [cSum]
		iSum = 0.00
		incomeEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) &  Entry.objects.filter(category__id=c.income.id)
		#total for associated Income category
		iCats += [c.income]
		for i in incomeEntries:
			iSum = iSum + float(i.amount)
		iTotals += [iSum]
		Totals += [iSum-cSum]
		linkNumber = linkNumber +1
	lN = range(7)
	
	cogsList = zip(lN,iCats,iTotals,cTotals,Totals)
	sortby(cogsList,4)
	h = 0
	for i in cogsList:
		cogsLabels += '{label: "' + i[1].name + '", v: ' + str(i[0]) + '}, '
	cogsLabels = cogsLabels.rstrip(', ')
	
	return render_to_response('incomevscost.html',locals(),context_instance=RequestContext(request))
				
@login_required
def moneyInMoneyOut(request):
	try:
		theMonth = int(request.GET['month'])
		theYear = int(request.GET['year'])
	except (KeyError):
		theMonth = datetime.now().month
		theYear = datetime.now().year
	
	(monthOptions,yearOptions) = htmlDates(theMonth,theYear)
	(report_start,report_end) = buildDates(theMonth,theYear)
	
	peeps = Person.objects.filter()
	ipeepsList = []
	incomeList = []
	epeepsList = []
	expenseList = []
	iLabels = ""
	eLabels = ""
	for p in peeps:
		iSum = 0.00
		incomeEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(category__type = 'INC') & Entry.objects.filter(name = p)
		for i in incomeEntries:
			iSum = iSum + float(i.amount)
		if iSum > 0:
			iLabels += '{label: "' + p.name + '", v: ' + str(p.id) + '}, '
			ipeepsList += [p]
			incomeList += [iSum]
			
		eSum = 0.00
		expenseEntries = Entry.objects.filter(date__gte = report_start) & Entry.objects.filter(date__lt = report_end) & Entry.objects.filter(Q(category__type = 'EXP') | Q(category__type = 'COGS')) & Entry.objects.filter(name = p)
		for e in expenseEntries:
			eSum = eSum + float(e.amount)
		if eSum > 0:
			eLabels += '{label: "' + p.name + '", v: ' + str(p.id) + '}, '
			epeepsList += [p]
			expenseList += [eSum]
		
		
	incomeMash = zip(ipeepsList,incomeList)
	sortby(incomeMash,1)
	expenseMash  = zip(epeepsList,expenseList)
	sortby(expenseMash,1)
	return render_to_response('moneyin-moneyout.html',locals(),context_instance=RequestContext(request))

@login_required
def balance(request):
	"""Track account balances over time."""
	#
	#Not fully implemented yet. 
	#Also needs different graphing library as Plotkit can't do negative numbers.
	#
	accounts = BankAccount.objects.filter(track_balance__exact=True)
	tally_labels = []
	tally = []

	this_id = -1
	this_account = 0
	for a in accounts:
		this_tally = []
		this_tally_labels = ""
		entries = Entry.objects.filter(bank_account__exact=a).order_by('date')
		transfers = AccountTransfer.objects.filter(bank_account__exact=a).order_by('date')
		total = a.initial_balance
		n = 0
		start = True
		for e in entries:
			if start:
				this_date = e.date
				start = False
			if this_date != e.date:
				this_tally.append([n,this_date,total])
				this_date = e.date
				this_tally_labels += '{label: "' + str(e.date) + '", v: ' + str(n) + '}, '
				n = n+1
			if e.category.type == 'COGS' or e.category.type == 'EXP':
				amount = -e.amount
			else:
				amount = e.amount
			total = total + amount
		this_tally.append([n,this_date,total])
		tally += [this_tally]
		this_date = e.date
		this_tally_labels += '{label: "' + str(e.date) + '", v: ' + str(n) + '}'
		tally_labels += [this_tally_labels]

	title = 'Account Balances'
	return render_to_response('account-balances.html',locals(),context_instance=RequestContext(request))			